/*
 * Copyright 2013-2024 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.kiki.stack.http.feign.SpringMvcContract;

import io.kiki.stack.http.feign.*;
import io.kiki.stack.http.feign.codec.Decoder;
import io.kiki.stack.http.feign.codec.Encoder;
import io.kiki.stack.http.feign.codec.ErrorDecoder;
import lombok.Getter;
import org.springframework.boot.context.properties.ConfigurationProperties;

import java.util.*;

@ConfigurationProperties("spring.cloud.openfeign.client")
public class FeignClientProperties {

    private boolean defaultToProperties = true;

    private String defaultConfig = "default";

    private Map<String, FeignClientConfig> config = new HashMap<>();

    /**
     * Feign clients do not encode slash `/` characters by default. To change this
     * behavior, set the `decodeSlash` to `false`.
     */
    private boolean decodeSlash = true;

    /**
     * If {@code true}, trailing slashes at the end of request urls will be removed.
     */
    private boolean removeTrailingSlash;

    public boolean isDefaultToProperties() {
        return defaultToProperties;
    }

    public void setDefaultToProperties(boolean defaultToProperties) {
        this.defaultToProperties = defaultToProperties;
    }

    public String getDefaultConfig() {
        return defaultConfig;
    }

    public void setDefaultConfig(String defaultConfig) {
        this.defaultConfig = defaultConfig;
    }

    public Map<String, FeignClientConfig> getConfig() {
        return config;
    }

    public void setConfig(Map<String, FeignClientConfig> config) {
        this.config = config;
    }

    public boolean isDecodeSlash() {
        return decodeSlash;
    }

    public void setDecodeSlash(boolean decodeSlash) {
        this.decodeSlash = decodeSlash;
    }

    public boolean isRemoveTrailingSlash() {
        return removeTrailingSlash;
    }

    public void setRemoveTrailingSlash(boolean removeTrailingSlash) {
        this.removeTrailingSlash = removeTrailingSlash;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        FeignClientProperties that = (FeignClientProperties) o;
        return defaultToProperties == that.defaultToProperties && Objects.equals(defaultConfig, that.defaultConfig) && Objects.equals(config, that.config) && Objects.equals(decodeSlash, that.decodeSlash) && Objects.equals(removeTrailingSlash, that.removeTrailingSlash);
    }

    @Override
    public int hashCode() {
        return Objects.hash(defaultToProperties, defaultConfig, config, decodeSlash, removeTrailingSlash);
    }

    /**
     * Feign client configuration.
     */
    public static class FeignClientConfig {

        @Getter
        private Logger.Level loggerLevel;

        @Getter
        private Integer connectTimeout;

        @Getter
        private Integer readTimeout;

        @Getter
        private Class<Retryer> retryer;

        @Getter
        private Class<ErrorDecoder> errorDecoder;

        @Getter
        private List<Class<RequestInterceptor>> requestInterceptors;

        @Getter
        private Class<ResponseInterceptor> responseInterceptor;

        @Getter
        private Map<String, Collection<String>> defaultRequestHeaders = new HashMap<>();

        @Getter
        private Map<String, Collection<String>> defaultQueryParameters = new HashMap<>();


        @Getter
        private Class<Decoder> decoder;

        @Getter
        private Class<Encoder> encoder;

        @Getter
        private Class<Contract> contract;

        @Getter
        private ExceptionPropagationPolicy exceptionPropagationPolicy;

        @Getter
        private List<Class<Capability>> capabilities;

        @Getter
        private Class<QueryMapEncoder> queryMapEncoder;

        @Getter
        private MicrometerProperties micrometer;

        private Boolean followRedirects;

        /**
         * Allows setting Feign client host URL. This value will only be taken into
         * account if the url is not set in the @FeignClient annotation.
         */
        @Getter
        private String url;

        public void setLoggerLevel(Logger.Level loggerLevel) {
            this.loggerLevel = loggerLevel;
        }

        public void setConnectTimeout(Integer connectTimeout) {
            this.connectTimeout = connectTimeout;
        }

        public void setReadTimeout(Integer readTimeout) {
            this.readTimeout = readTimeout;
        }

        public void setRetryer(Class<Retryer> retryer) {
            this.retryer = retryer;
        }

        public void setErrorDecoder(Class<ErrorDecoder> errorDecoder) {
            this.errorDecoder = errorDecoder;
        }

        public void setRequestInterceptors(List<Class<RequestInterceptor>> requestInterceptors) {
            this.requestInterceptors = requestInterceptors;
        }

        public void setResponseInterceptor(Class<ResponseInterceptor> responseInterceptor) {
            this.responseInterceptor = responseInterceptor;
        }

        public void setDefaultRequestHeaders(Map<String, Collection<String>> defaultRequestHeaders) {
            this.defaultRequestHeaders = defaultRequestHeaders;
        }

        public void setDefaultQueryParameters(Map<String, Collection<String>> defaultQueryParameters) {
            this.defaultQueryParameters = defaultQueryParameters;
        }


        public void setDecoder(Class<Decoder> decoder) {
            this.decoder = decoder;
        }

        public void setEncoder(Class<Encoder> encoder) {
            this.encoder = encoder;
        }

        public void setContract(Class<Contract> contract) {
            this.contract = contract;
        }

        public void setExceptionPropagationPolicy(ExceptionPropagationPolicy exceptionPropagationPolicy) {
            this.exceptionPropagationPolicy = exceptionPropagationPolicy;
        }

        public void setCapabilities(List<Class<Capability>> capabilities) {
            this.capabilities = capabilities;
        }

        public void setQueryMapEncoder(Class<QueryMapEncoder> queryMapEncoder) {
            this.queryMapEncoder = queryMapEncoder;
        }

        public void setMicrometer(MicrometerProperties micrometer) {
            this.micrometer = micrometer;
        }

        public Boolean isFollowRedirects() {
            return followRedirects;
        }

        public void setFollowRedirects(Boolean followRedirects) {
            this.followRedirects = followRedirects;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            FeignClientConfig that = (FeignClientConfig) o;
            return loggerLevel == that.loggerLevel && Objects.equals(connectTimeout, that.connectTimeout) && Objects.equals(readTimeout, that.readTimeout) && Objects.equals(retryer, that.retryer) && Objects.equals(errorDecoder, that.errorDecoder) && Objects.equals(requestInterceptors, that.requestInterceptors) && Objects.equals(responseInterceptor, that.responseInterceptor) && Objects.equals(encoder, that.encoder) && Objects.equals(decoder, that.decoder) && Objects.equals(contract, that.contract) && Objects.equals(exceptionPropagationPolicy, that.exceptionPropagationPolicy) && Objects.equals(defaultRequestHeaders, that.defaultRequestHeaders) && Objects.equals(defaultQueryParameters, that.defaultQueryParameters) && Objects.equals(capabilities, that.capabilities) && Objects.equals(queryMapEncoder, that.queryMapEncoder) && Objects.equals(micrometer, that.micrometer) && Objects.equals(followRedirects, that.followRedirects) && Objects.equals(url, that.url);
        }

        @Override
        public int hashCode() {
            return Objects.hash(loggerLevel, connectTimeout, readTimeout, retryer, errorDecoder, requestInterceptors, responseInterceptor, encoder, decoder, contract, exceptionPropagationPolicy, defaultQueryParameters, defaultRequestHeaders, capabilities, queryMapEncoder, micrometer, followRedirects, url);
        }

    }

    /**
     * Micrometer configuration for Feign Client.
     */
    public static class MicrometerProperties {

        private Boolean enabled = true;

        public Boolean getEnabled() {
            return enabled;
        }

        public void setEnabled(Boolean enabled) {
            this.enabled = enabled;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }

            MicrometerProperties that = (MicrometerProperties) o;
            return Objects.equals(enabled, that.enabled);
        }

        @Override
        public int hashCode() {
            return Objects.hash(enabled);
        }

    }

}
